import random
import astar
import defaultvalue

##################################################
# the same structure that any search route al has
##################################################
class SearchAlg:
    def __init__(self, turn_num, destination = None, reverse = False):
        self.reset()
        self.status = 0 # start status of FSM
        self.reset_time = defaultvalue.default_resettime # force status to change per reset_time 
        self.turn_num = turn_num # nums of turn_points
        self.start_position = [-1, -1]
        self.destination = destination
        self.turn_points = [] # points that tanker moves to before it move to the destination
        # whether a position that must be reached exists
        if self.destination != None:
            self.des_num = 1
        else:
            self.des_num = 0
        # move to the first turn_points or move to the ditination, when start
        self.reverse = reverse

    def set_destination(self, destination):
        """ set tanker's destination """
        self.destination = destination
        self.des_num = 1

    def set_reverse(self, reverse):
        """ move to the distination or move to the first turn point, when start """
        self.reverse = reverse
        if reverse == True:
            self.status = self.turn_num # move to the distination first
            
    def reset(self):
        """ reset map's status """
        self.map = []
        self.way = []

    def calculate_ends(self, destination):
        """ tanker may need to calculate its new destination, if it wants to evade the player """
        x = 0
        y = 0
        if destination[0] <= int(self.length/2):
            if destination[1] <= int(self.height/2): # the topleft area
                x = destination[0] + int(self.length/2) - 1
                y = destination[1]
            else: # the topright area
                x = destination[0]
                y = destination[1] - int(self.height/2)
        else:
            if destination[1] <= int(self.height/2): # the bottomleft area
                x = destination[0]
                y = destination[1] + int(self.height/2) - 1
            else: # the bottomright area
                x = destination[0] - int(self.length/2)
                y = destination[1]
        # adjust the position until it is not occupied
        while 1:
            if self.map[x][y] == 0:
                self.destination[0] = x
                self.destination[1] = y
                break
            else:
                x += random.randint(-1, 1)
                y += random.randint(-1, 1)
                if x < 0:
                    x = 0
                if y < 0:
                    y = 0
                if x > self.length - 1:
                    x = self.length -1
                if y > self.height - 1:
                    y = self.height -1        

    def produce_turns(self):
        """ produce turnpoints when algorithm starts or a new loop starts """        
        # need more turnpoints
        if self.turn_num > 0 and len(self.turn_points) - self.des_num < self.turn_num:
            while 1:
                if len(self.turn_points) - self.des_num >= self.turn_num:
                    break
                turn_pos = []
                turn_pos.append(random.randint(0, self.length - 1))
                turn_pos.append(random.randint(0, self.height - 1))
                if self.map[turn_pos[0]][turn_pos[1]] == 0:
                    self.turn_points.append(turn_pos)
        # if one definite destination exists, add it to the turnpoints list
        if self.des_num:
            # first start, list doesn't have a destination point
            if len(self.turn_points) < self.turn_num + self.des_num :
                self.turn_points.append(self.destination)
            # already have one destination point, remove the old one, insert the new one, mainly works for enemy1
            else:
                self.turn_points.pop(-1)
                self.turn_points.append(self.destination)

    def adjust_turns(self, start_position):
        """ when a tanker doesn't move for a while, change its current object """
        if self.start_position == start_position: # stop happen
            # tanker type is moving to destination first
            if self.reverse == True:
                # now moving to the destination
                if self.status > self.turn_num :
                    self.status = 0 # status of FSM become 0
                # random move for a while
                if self.status != self.turn_num:
                    while 1 :
                        x = random.randint(0, self.length - 1)
                        y = random.randint(0, self.height - 1)
                        if self.map[x][y] == 0:
                            break
                        self.turn_points[self.status] = [x , y]
            # tanker type is moving to the turnpoints first
            elif self.status != self.turn_num: # move to a turnpoint, not the destination
                # change current turnpoint, not the difinite destination
                while 1 :
                    x = random.randint(0, self.length - 1)
                    y = random.randint(0, self.height - 1)
                    if self.map[x][y] == 0:
                        break
                self.turn_points[self.status] = [x , y]

    def search(self, map, start_position, end_position):
        """ init the algorithm, and find a route """
        self.reset()        
        self.map = map
        self.length = len(map)
        self.height = len(map[0])
        # move to the distination first and want to evade the player. Distination needs to be calculated
        if self.reverse == True:
            self.calculate_ends(end_position)
        # move to a turnpoint first
        else:
            self.destination = end_position
            
        # check turnpoints acount meet needs and check whether blocked happens
        self.produce_turns()
        self.adjust_turns(start_position)        
        
        self.start_position = start_position          
        self.destination = self.turn_points[self.status] # current destination is current turnpoint

        search_al = astar.AStar()

        # force status to change, except that tanker is moving to its difinite destination                    
        self.reset_time -= 1
        if self.status < self.turn_num and self.reset_time <= 0: # move to a turnpoint
            self.reset_time = defaultvalue.default_resettime
            self.status += 1
        elif start_position == self.destination: # reach current destination
            self.status += 1
        if self.des_num == 0 and self.status >= self.turn_num: # no destination, random move again
            self.status = 0
            self.turn_points = []
            # random move for a while
            while 1:
                temp_turns = []
                temp_turns.append(random.randint(0, self.length - 1))
                temp_turns.append(random.randint(0, self.height - 1))
                if self.map[temp_turns[0]][temp_turns[1]] == 0:
                    break
            self.way = search_al.search(self.map, self.start_position, temp_turns)
            return self.way
               
        self.way = search_al.search(self.map, self.start_position, self.destination)
        return self.way
